﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using ToracTechnologies.ExpressionTreeLibrary.DynamicApi.Remappers;
using ToracTechnologies.ExpressionTreeLibrary.Remappers;

namespace ToracTechnologies.ExpressionTreeLibrary.ExtensionMethods.ExpressionTrees
{

    /// <summary>
    /// Linq Expression's Extension Methods
    /// </summary>
    public static class LinqExpressionExtMethods
    {

        /// <summary>
        /// Merges the member initialization list of two lambda expressions into one.
        /// </summary>
        /// <typeparam name="TSource">Source type.</typeparam>
        /// <typeparam name="TBaseDest">Resulting type of the base mapping expression. TBaseDest is  typically a super class of TExtendedDest</typeparam>
        /// <typeparam name="TExtendedDest">Resulting type of the extended mapping expression.</typeparam>
        /// <param name="BaseExpression">The base mapping expression, containing a member  initialization expression.</param>
        /// <param name="MergeExpression">The extended mapping expression to be merged into the base member initialization expression.</param>
        /// <param name="MergePosition">Where do you want to merge the second expression. Before Or After The Base (First Expression)</param>
        /// <returns>Resulting expression, after the merged select expression has been applied.</returns>
        public static Expression<Func<TSource, TExtendedDest>> Merge<TSource, TBaseDest, TExtendedDest>(this Expression<Func<TSource, TBaseDest>> BaseExpression, Expression<Func<TSource, TExtendedDest>> MergeExpression, ExpressionRemapperShared.ExpressionMemberInitMergerPosition MergePosition)
        {
            //Use an expression visitor to perform the merge of the select expressions.
            ExpressionMemberInitMerger<TSource, TBaseDest, TExtendedDest> thisVisitor = new ExpressionMemberInitMerger<TSource, TBaseDest, TExtendedDest>(BaseExpression, MergePosition);

            //now build a new expression and return it
            return (Expression<Func<TSource, TExtendedDest>>)thisVisitor.Visit(MergeExpression);
        }

        /// <summary>
        /// Merges a sub property which is an object of a member initialization list of two lambda expressions into one.
        /// </summary>
        /// <typeparam name="TSource">Source type.</typeparam>
        /// <typeparam name="TBaseDest">Resulting type of the base mapping expression. TBaseDest is typically a super class of TExtendedDest</typeparam>
        /// <typeparam name="TExtendedDest">Resulting type of the extended mapping expression.</typeparam>
        /// <typeparam name="TPropertySubClassType">Property Type Selector For The Sub Class Property Name</typeparam>
        /// <param name="BaseExpression">The base mapping expression, containing a member initialization expression.</param>
        /// <param name="MergeExpression">The extended mapping expression to be merged into the base member initialization expression.</param>
        /// <param name="PropertyNameOfSubClass">Holds the property name of the sub class off of the base class. So we will use reflection to grab this property name off of the base object</param>
        /// <param name="MergeSubObjectPosition">Where do you want to merge the second expression. Before Or After The Base (First Expression)</param>
        /// <returns>Resulting expression, after the merged select expression has been applied.</returns>
        /// <remarks>See full documentation example in the method</remarks>
        public static Expression<Func<TSource, TBaseDest>> MergeSubObject<TSource, TBaseDest, TExtendedDest, TPropertySubClassType>(this Expression<Func<TSource, TBaseDest>> BaseExpression, Expression<Func<TSource, TExtendedDest>> MergeExpression, Expression<Func<TBaseDest, TPropertySubClassType>> PropertyNameOfSubClass, ExpressionRemapperShared.ExpressionMemberInitMergerPosition MergeSubObjectPosition)
        {
            //Use an expression visitor to perform the merge of the select expressions.
            var thisVisitor = new ExpressionMemberInitSubPropertyObjectMerger<TSource, TBaseDest, TPropertySubClassType>(BaseExpression, PropertyNameOfSubClass, MergeSubObjectPosition);

            //go visit the expression (don't absorb the result, we don't need, we will grab the result from the final result property)
            thisVisitor.Visit(MergeExpression);

            //use the final result property and return it
            return thisVisitor.FinalResult;
        }

    }

}
